
#include "arch_tabla_dispersion.h"

ArchTablaDispersion::ArchTablaDispersion(std::string& nombre, unsigned int tam_bloque):ArchControl(nombre, tam_bloque){
	this->size = 0;
}
	
ArchTablaDispersion::~ArchTablaDispersion(){

}

void ArchTablaDispersion::crear() throw(ExcepCreandoArch){
	ArchControl::crear();
	this->size = 0;
}
		
void ArchTablaDispersion::abrir() throw(ExcepAbriendoArch){
	ArchControl::abrir();
	this->size = get_size_desde_arch();
}

void ArchTablaDispersion::append(unsigned int valor) throw(ExcepArchCerrado, ExcepEscribirBloque,ExcepLeerBloque){
	ArchControl::append(valor);
	this->size++;
}
		
void ArchTablaDispersion::borrar_ultimo(){
	ArchControl::borrar_ultimo();
	this->size--;	
}
				
void ArchTablaDispersion::duplicar() throw(ExcepArchCerrado, ExcepEscribirBloque,ExcepLeerBloque){
	unsigned int num_entrada = 0;
	unsigned int valor;
	unsigned int size_aux = this->size;
	while (num_entrada < size_aux) {
		valor = this->get_valor(num_entrada);
		this->append(valor);
		num_entrada++;
	}
}

bool ArchTablaDispersion::dividir() throw(ExcepArchCerrado, ExcepEscribirBloque,ExcepLeerBloque){
	unsigned int cont = 0;
	unsigned int size_aux = this->size / 2;
	if (this->es_simetrica()) {
		while (cont < size_aux) {
			this->borrar_ultimo();
			cont++;	
		}
		return true;
	}
	return false;
}
		
void ArchTablaDispersion::actualizar_circular (unsigned int num_entrada, unsigned int cant, unsigned int valor)
									throw(ExcepArchCerrado, ExcepEscribirBloque,ExcepLeerBloque){
	unsigned int cont = num_entrada;
	unsigned int resto;
	bool termine = false;
	bool di_vuelta = false;
	while (!termine) {
		this->set_valor(cont, valor);
		if (cont + cant >= this->size) {
			resto = cont + cant - this->size;
			cont = resto;
			di_vuelta = true;	
		} else
			cont += cant;
		if ((di_vuelta) && (cont >= num_entrada))
			termine = true;
	}
}
									
unsigned int ArchTablaDispersion::get_size(){
	return this->size;
} 

unsigned int ArchTablaDispersion::get_espacio_libre() throw(ExcepArchCerrado, ExcepLeerBloque){
	unsigned int tam_libre = 0;
	unsigned int cont = 0;
	Bloque* bloque_actual = NULL;
	
	while (cont < cant_bloques){ //recorro todos los bloques
		try{
			bloque_actual = Buffer::get_instance()->get_bloque(*this,cont);
			tam_libre += bloque_actual->get_espacio_libre();
		}catch (ExcepLeerBloque &e){
			if (bloque_actual != NULL) { 
				delete (bloque_actual); 
				bloque_actual = NULL;
			}
			throw e;	//la sigo propagando por ahora
		}
		if (bloque_actual != NULL) { 
			delete (bloque_actual); 
			bloque_actual = NULL;
		}
		cont++;
	}
	return tam_libre;
}
 		
unsigned int ArchTablaDispersion::get_cant_regs() throw(ExcepArchCerrado){
	unsigned int cont = 0;
	Bloque* bloque_actual;
	
	for(unsigned int i = 0; i < this->cant_bloques; i++ ){
		//obtengo el bloque nro i desde el buffer
		bloque_actual = Buffer::get_instance()->get_bloque(*this,i);
		//le pido al bloque la cant de registros contenidos en el mismo
		cont += get_cant_regs_bloque(bloque_actual);
		//Borro el bloque que me devolvio el buffer
		delete (bloque_actual);
	}
	return cont;
}
 		
bool ArchTablaDispersion::es_simetrica(){
	unsigned int cont = 0;
	unsigned int mitad = this->size /2;
	unsigned int valor1, valor2;
	bool simetrica = true;
	while ((cont < mitad) && (simetrica)) {
		valor1 = this->get_valor(cont);
		valor2 = this->get_valor(cont + mitad);
		simetrica = (valor1 == valor2);
		cont++;
	}
	return simetrica;
}

unsigned int ArchTablaDispersion::get_size_desde_arch(){
	unsigned int resultado = 0;
	unsigned int cant_ultimo;
	unsigned int cant_por_bloque;
	Bloque* ultimo = Buffer::get_instance()->get_bloque(*this, this->cant_bloques - 1);
	cant_ultimo = (ultimo->get_tope() / TAM_REG);
	cant_por_bloque = (ultimo->get_size() / TAM_REG) - 1;
	resultado = cant_por_bloque * (this->cant_bloques - 1) + cant_ultimo - 1;
	delete (ultimo);
	return resultado;
}

unsigned int ArchTablaDispersion::get_cant_regs_bloque(Bloque* bloque){
	unsigned int esp_ocupado_por_regs = bloque->get_tope();
	if (bloque->get_num_bloque() == 0)
		esp_ocupado_por_regs -= TAM_REG;
	return (esp_ocupado_por_regs / TAM_REG);
}
